﻿// tetris.cpp : 定义应用程序的入口点。
//

/*
记录于2016-5-29 星期日    由于闪屏过于显眼，采用双缓冲

*/

#include "stdafx.h"
#include "Tetris.h"
#include "windows.h"
#include <mmsystem.h>//windows中与多媒体有关的大多数接口的头文件
#pragma comment(lib, "WINMM.LIB")//告诉编译器要导入winnm库；WINMM.LIB是windows多媒体相关应用程序接口，用于低档的音频和游戏手柄
#define MAX_LOADSTRING 100// MAX_LOADSTRING:宏定义 最大字符串长度

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);//ATOM:宏定义 word短整型 窗口注册函数
BOOL                InitInstance(HINSTANCE, int);//BOOL:布尔值 初始化应用程序
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);//窗口过程处理函数
/* LRESULT:宏定义 LONG_PTR 长整型
	CALLBACK:宏定义 __stdcall
	UINT:类型定义 unsigned int类型
	WPARAM:宏定义 字参数  UINT_PTR 类型定义 unsigned int类型
	LPARAM:宏定义 长字参数 LONG_PTR 类型定义 long类型 */
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

// 全局变量:
HINSTANCE hInst;                                // 当前实例
TCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
HMENU diff;//难度菜单句柄定义
HMENU lay;//布局菜单句柄定义
#define IDM_DIFF 106
#define ID_dif1 111
#define ID_dif2 112
#define ID_dif3 113
#define ID_LAYOUT1 114
#define ID_LAYOUT2 115

/*APIENTRY:宏定义 WINAPI 类型定义 __stdcall
	_tWinMain:宏定义 wWinMain
	WinMain：应用程序入口点
	参数：hInstance：应用程序当前实例句柄
	hPrevInstance：应用程序其它实例句柄
	lpCmdLine：将命令行参数作为unicode字符串包含
	nCmdShow：表示主应用程序窗口是否将最小化、最大化或正常显示的标志
*/
int APIENTRY _tWinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)
{
	init_game();//游戏初始化
	/*UNREFERENCED_PARAMETER:宏定义 #define UNREFERENCED_PARAMETER(P)
	功能；避免编译器关于未引用参数的警告
	*/
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	// TODO: 在此放置代码。
	MSG msg;//MSG：结构体
	HACCEL hAccelTable;//HACCEL：宏定义 加速键句柄

	// 初始化全局字符串
	/*函数LoadString：加载字符串
	函数原型及参数注释
	int WINAPI LoadString(
	_In_opt_ HINSTANCE hInstance,//应用程序实例句柄
	_In_     UINT      uID,//资源中的字符串编号
	_Out_    LPTSTR    lpBuffer,//接收从资源里拷贝字符串出来的缓冲区
	_In_     int       nBufferMax//指明缓冲区的大小
	);
	*/
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);//加载标题
	LoadString(hInstance, IDC_TETRIS, szWindowClass, MAX_LOADSTRING);//加载主窗口类名
	MyRegisterClass(hInstance);//注册窗口类

	// 执行应用程序初始化:
	/*函数InitInstance
	功能：创建窗口
	如果初始化成功，则返回非零值；否则返回0
	*/
	if (!InitInstance(hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TETRIS));//加载指定的加速键表

	// 主消息循环:
	while (1)
	{
		
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))//为一个消息检查线程消息队列，并将该消息（如果存在）放于指定的结构
		{
			TranslateMessage(&msg);//将虚拟键消息转换为字符消息，字符消息被送到调用线程的消息队列中，在下一次线程调用函数GetMessage时被读出
			DispatchMessage(&msg);//派发消息到窗口程序，传给窗体函数处理
			if (msg.message == WM_QUIT)//结束消息循环
			{
				break;
			}
		}
		else
		{
			if ((GAME_STATE & 2) != 0)
			{
				/*函数GetTickCount
				返回从操作系统启动到当前所经过的毫秒数，常常用来判断某个方法执行的时间
				函数原型是DWORD GetTickCount(void)，返回值以32位的双字类型DWORD存储
				*/
				tCur = GetTickCount();//返回（retrieve）从操作系统启动所经过（elapsed）的毫秒数
				if (tCur - tPre>g_speed)//超过下落时间
				{
					int flag = CheckValide(curPosX, curPosY + 1, bCurTeris);//判断下一行的情况，返回给flag
					if (flag == 1)//正常下降一行
					{
						curPosY++;//方块纵坐标y+1
						tPre = tCur;//重新计算反应时间
						HWND hWnd = GetActiveWindow();//获得与调用线程的消息队列相关的活动窗口的窗口句柄
						/*函数InvalidateRect:
						向指定的窗体更新区域添加一个矩形，然后窗口客户区域的这一部分将被重新绘制
						*/
						InvalidateRect(hWnd, &rc_left, FALSE);
						InvalidateRect(hWnd, &rc_right_top, FALSE);				
					}
					else if (flag == -2)//方块到底时
					{
						g_speed = t_speed;//速度重置
						fillBlock();//到达底部后填充矩阵
						checkComplite();//查看一行是否能消去  采用从上往下的消法，消去一行后把上面的每行都往下移
						setRandomT();//随机生成一个方块用作下一次掉落
						curPosX = (NUM_X - 4) >> 1;//重新设置方块横坐标x
						curPosY = 0;//重新设置方块纵坐标y
						HWND hWnd = GetActiveWindow();
						InvalidateRect(hWnd, &rc_main, FALSE);
					}
					else if (flag == -3)
					{
						HWND hWnd = GetActiveWindow();//获得与调用线程的消息队列相关的活动窗口的窗口句柄
						if (MessageBox(hWnd, L"胜败乃兵家常事，菜鸡请重新来过", L":时光机", MB_YESNO) == IDYES)//给出消息提示（游戏失败）
						{
							init_game();//重新开始游戏
						}
						else
						{
							break;//退出游戏
						}
					}
				}
			}
		}
	}

	return (int)msg.wParam;//程序终止时将信息返回系统
}


//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
//  注释:
//
//    仅当希望
//    此代码与添加到 Windows 95 中的“RegisterClassEx”
//    函数之前的 Win32 系统兼容时，才需要此函数及其用法。调用此函数十分重要，
//    这样应用程序就可以获得关联的
//    “格式正确的”小图标。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;//WNDCLASSEXW：窗口类结构

	wcex.cbSize = sizeof(WNDCLASSEX);//窗口类的大小

	wcex.style = CS_HREDRAW | CS_VREDRAW;//设置窗口的样式
	wcex.lpfnWndProc = WndProc;//窗口处理函数为(WNDPROC)WndProc
	wcex.cbClsExtra = 0;//窗口类额外字节数（通常为0：窗口类无扩展）
	wcex.cbWndExtra = 0;// 窗口实例额外字节数（通常为0：窗口实例无扩展）
	wcex.hInstance = hInstance;//当前实例句柄
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TETRIS));//指定窗口类的图标句柄（默认图标）
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);//指定窗口类的光标句柄（箭头光标）
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//指定窗口类的背景画刷句柄
	wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TETRIS);
	wcex.lpszClassName = szWindowClass;//指定窗口类的名字
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));//标识某个窗口最小化时显示的图标

	return RegisterClassEx(&wcex);//调用RegisterClassEx()函数向系统注册窗口类 
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HWND hWnd;//HWND:宏定义 用以检索消息的窗口句柄

	hInst = hInstance; // 将实例句柄存储在全局变量中
	/*函数CreateWindow
	功能：创建一个窗口类的实例
	函数原型及参数注释
	HWND WINAPI CreateWindow(
	_In_opt_  LPCTSTR lpClassName,//窗口类名称
	_In_opt_  LPCTSTR lpWindowName,//窗口标题
    _In_      DWORD dwStyle,//窗口风格，或称窗口格式
	_In_      int x,//初始 x 坐标
	_In_      int y,//初始 y 坐标
	_In_      int nWidth,//初始 x 方向尺寸
	_In_      int nHeight,//初始 y 方向尺寸
	_In_opt_  HWND hWndParent,//父窗口句柄
	_In_opt_  HMENU hMenu,//窗口菜单句柄
	_In_opt_  HINSTANCE hInstance,//创建程序实例句柄
	_In_opt_  LPVOID lpParam//创建参数（指向一个传递给窗口的参数值的指针）
	);
*/
	hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
	if (!hWnd)//如果创建窗口失败
	{
		return FALSE;
	}
	/*函数ShowWindow
	功能：在屏幕上显示窗口
	参数：hWnd为窗口句柄;nCmdShow为窗口显示形式标识
	*/
	ShowWindow(hWnd, nCmdShow);
	/*函数UpdateWindow
	功能：更新并绘制用户区
	参数：hWnd为窗口句柄
	*/
	UpdateWindow(hWnd);

	return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
	/*函数 WndProc
	功能：Wndproc是Windows操作系统向应用程序发送一系列消息之一，每个窗口会有一个窗口过程的回调函数
	参数：窗口句柄(Window Handle) HWND 当前接收消息的窗口句柄，
	消息ID(Message ID) UINT被传送过来的消息
	WPARAM wParam和LPARAM lParam是包含有关消息的附加信息
	*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;//PAINTSTRUCT：结构体 Windows系统提供的标识无效区域的结构
	HDC hdc;//标识设备环境句柄
	int nWinx, nWiny, nClientX, nClientY;
	int posX, posY;
	RECT rect;//RECT：结构体 定义了一个矩形区域
	HMENU hSysmenu;//标识菜单句柄
	switch (message)
	{
	/*消息WM_CREATE
	由CreateWindow函数发出的消息
	*/
	case WM_CREATE:

		/*函数GetWindowRect
		功能：返回指定窗口的边框矩形的尺寸，该尺寸以相对于屏幕坐标左上角的屏幕坐标给出
		函数原型：BOOL GetWindowRect（HWND hWnd，LPRECTlpRect）；
		参数：
		hWnd:窗口句柄
		lpRect：指向一个RECT结构的指针，该结构接收窗口的左上角和右下角的屏幕坐标
		返回值：如果函数成功，返回值为非零；如果函数失败，返回值为零
		*/
		GetWindowRect(hWnd, &rect);
		nWinx = 530;//设置窗口宽度
		nWiny = 680;//设置窗口高度
		posX = GetSystemMetrics(SM_CXSCREEN);//获取屏幕宽度
		posY = GetSystemMetrics(SM_CYSCREEN);//获取屏幕高度
		posX = (posX - nWinx) >> 1;//计算出居中放置的X坐标位置
		posY = (posY - nWiny) >> 1;//计算出居中放置的Y坐标位置
		GetClientRect(hWnd, &rect);//获取窗口客户区的大小（窗口中除标题栏、菜单栏之外地方）
		nClientX = rect.right - rect.left;//用户区域宽度
		nClientY = rect.bottom - rect.top;//用户区域高度

		MoveWindow(hWnd, posX, posY, 530, 680, TRUE);//修改窗口大小
		//hSysmenu = GetSystemMenu(hWnd, false);//获取菜单句柄
		hSysmenu = GetMenu(hWnd);//获取菜单句柄
		AppendMenu(hSysmenu, MF_SEPARATOR, 0, NULL);//添加一条水平分割线
		/*AppendMenu(hSysmenu, 0, IDM_DIFF, L"难度选择");
		break;这里有问题。。*/
		diff = CreatePopupMenu();//创建一个难度菜单句柄
		AppendMenu(hSysmenu, MF_POPUP, (UINT_PTR)diff, L"难度选择");//添加菜单diff
		AppendMenu(diff, MF_STRING, ID_dif1, L"难度1");//在新添加的菜单下创建菜单项难度1
		AppendMenu(diff, MF_STRING, ID_dif2, L"难度2");//在新添加的菜单下创建菜单项难度2
		AppendMenu(diff, MF_STRING, ID_dif3, L"难度3");//在新添加的菜单下创建菜单项难度3
		lay = CreatePopupMenu();//创建一个布局菜单句柄
		AppendMenu(hSysmenu, MF_POPUP, (UINT_PTR)lay, L"布局选择");//添加菜单lay
		AppendMenu(lay, MF_STRING, ID_LAYOUT1, L"布局1");//在新添加的菜单下创建菜单项布局1
		AppendMenu(lay, MF_STRING, ID_LAYOUT2, L"布局2");//在新添加的菜单下创建菜单项布局2
		SetMenu(hWnd, hSysmenu);//设置系统自定义菜单
		SetMenu(hWnd, diff);//设置添加的难度菜单
		SetMenu(hWnd, lay);//设置添加的布局菜单
		//selectDiffculty(hWnd, 1);
		break;
	/*消息WM_COMMAND
	处理对话框消息（当用户从菜单选中一个命令项目、当一个控件发送通知消息给去父窗口或者按下一个快捷键将发送 WM_COMMAND 消息）
	*/
	case WM_COMMAND:
		wmId = LOWORD(wParam);//从此宏从指定的32位值，检索低顺序单词
		wmEvent = HIWORD(wParam);//获取高位字节
		// 分析菜单选择:
		switch (wmId)
		{
		case IDM_ABOUT:  //关于
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);//弹出对话框
			break;
		case IDM_EXIT:	//退出
			DestroyWindow(hWnd);//退出系统
			break;
		case ID_dif1:
			selectDiffculty(hWnd, 1);//选择难度1
			break;
		case ID_dif2:
			selectDiffculty(hWnd, 2);//选择难度2
			break;
		case ID_dif3:
			selectDiffculty(hWnd, 3);//选择难度3
			break;
		case ID_LAYOUT1:
			selectLayOut(hWnd, 1);//选择布局1
			break;
		case ID_LAYOUT2:
			selectLayOut(hWnd, 2);//选择布局2
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam); //该函数调用缺省的窗口过程来为应用程序没有处理的任何窗口提供缺省的处理,该函数确保每一个消息得到处理
		}
		break;
	/*消息WM_KEYDOWN
	按下一个非系统键时产生的消息
	*/
	case WM_KEYDOWN://键盘消息
		hdc = GetDC(hWnd);
		InvalidateRect(hWnd, NULL, false);
		switch (wParam)
		{
		case VK_LEFT://左移
			curPosX--;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX++;
			}
			break;
		case VK_RIGHT://右移
			curPosX++;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX--;
			}
			break;
		case VK_UP://旋转
			RotateTeris(bCurTeris);
			break;
		case VK_DOWN://加速下降
			if (g_speed == t_speed)
				g_speed = 10;
			else
				g_speed = t_speed;
			//outPutBoxInt(g_speed);
			break;
		case 'W'://旋转
			RotateTeris(bCurTeris);
			break;
		case 'A'://左移
			curPosX--;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX++;
			}
			break;
		case 'D'://右移
			curPosX++;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX--;
			}
			break;

		case 'S'://加速下降
			if (g_speed == t_speed)
				g_speed = 10;
			else
				g_speed = t_speed;
			//outPutBoxInt(g_speed);
			break;
		default:
			break;
		}
	/*消息WM_PAINT
	当需要绘制一部分应用窗口的时候，这个消息被Windows或者其他应用程序绘制调用*/
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);//HDC 标识设备环境句柄
		// TODO: 在此添加任意绘图代码...
		DrawBackGround(hdc);//构造函数指定的边界范围内用适当的颜色绘制背景
		EndPaint(hWnd, &ps);//标记指定窗口的绘图结束
		break;
	/*消息WM_DESTROY
	窗口销毁后(调用DestroyWindow()后)，消息队列得到的消息
	*/
	case WM_DESTROY:
		PostQuitMessage(0);//调用PostQuitMessage发出WM_QUIT消息
		break;
	default://默认时采用系统消息默认处理函数
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);//避免编译器关于未引用参数的警告
	switch (message)
	{
	case WM_INITDIALOG://对话框及其所有子控件都创建完毕
		return (INT_PTR)TRUE;

	case WM_COMMAND://处理应用程序菜单
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));//该函数清除一个模态对话框,并使系统中止对对话框的任何处理
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}


void drawBlocked(HDC mdc)//绘制当前已经存在砖块的区域
{
	int i, j;

	HBRUSH hBrush = (HBRUSH)CreateSolidBrush(RGB(255, 255, 0));//取黄色画刷

	SelectObject(mdc, hBrush);//将画刷选入指定mdc

	for (i = 0; i<NUM_Y; i++)
	{
		for (j = 0; j<NUM_X; j++)
		{
			if (g_hasBlocked[i][j])
			{
				Rectangle(mdc, BORDER_X + j*BLOCK_SIZE, BORDER_Y + i*BLOCK_SIZE,
					BORDER_X + (j + 1)*BLOCK_SIZE, BORDER_Y + (i + 1)*BLOCK_SIZE
				);//绘画矩形的函数，并用当前颜色的画刷进行颜色的填充
			}
		}
	}
	DeleteObject(hBrush);//删除画刷
}

int CheckValide(int startX, int startY, BOOL bTemp[4][4])//检查方块是否合法
{
	int i, j;
	for (i = 3; i >= 0; i--)
	{
		for (j = 3; j >= 0; j--)
		{
			if (bTemp[i][j])
			{
				if (j + startX<0 || j + startX >= NUM_X)
				{
					return -1;
				}
				if (i + startY >= NUM_Y)
				{
					return -2;
				}
				if (g_hasBlocked[i + startY][j + startX])
				{
					//outPutBoxInt(j+startY);
					if (curPosY == 0)
					{
						return -3;
					}
					return -2;
				}
			}
		}
	}
	//MessageBox(NULL,L"这里",L"as",MB_OK);
	//outPutBoxInt(curPosY);
	return 1;
}

void checkComplite()//查看一行是否能消去  采用从上往下的消法，消去一行后把上面的每行都往下移
{
	int i, j, k, count = 0;
	for (i = 0; i<NUM_Y; i++)
	{
		bool flag = true;
		for (j = 0; j<NUM_X; j++)
		{
			if (!g_hasBlocked[i][j])
			{
				flag = false;//有一个为空时，不能消去
			}
		}
		if (flag)
		{
			count++;
			for (j = i; j >= 1; j--)//从上往下把每行下移
			{
				for (k = 0; k<NUM_X; k++)
				{
					g_hasBlocked[j][k] = g_hasBlocked[j - 1][k];
				}

			}
			drawCompleteParticle(i);//在消去的那行画一行空白做出消去的效果
			Sleep(300);//暂停300毫秒

			PlaySound(_T("coin.wav"), NULL, SND_FILENAME | SND_ASYNC);//消去一行时播放系统自带提示音
		}
	}
	GAME_SCORE += int(count*1.5);//单行+1分，两行+3分，三行+4分，四行6分，以此类推

}



VOID outPutBoxInt(int num)//自定义的弹窗函数  用于调试
{
	TCHAR szBuf[1024];
	LPCTSTR str = TEXT("%d");
	/*函数wsprintf
	功能：将一系列的字符和数值输入到缓冲区
	函数原型：int __cdecl wsprintf(_Out_ LPTSTR lpOut,_In_ LPCTSTR lpFmt,_In_...);
	参数：lpOut [out]类型: LPTSTR 接收缓冲区格式化输出 缓冲区最大为 1,024 bytes.
		  lpFmt [in]类型: LPCTSTR 控制输出的格式.
		  ... [in]可变参数
	*/
	wsprintf(szBuf, str, num);
	MessageBox(NULL, szBuf, L"aasa", MB_OK);//弹出消息框
}

VOID outPutBoxString(TCHAR str[1024])
{
	TCHAR szBuf[1024];
	LPCTSTR cstr = TEXT("%s");
	wsprintf(szBuf, cstr, str);
	MessageBox(NULL, szBuf, L"aasa", MB_OK);
}




void setRandomT()//随机生成一个方块用作下一次掉落
{
	int rd_start = RandomInt(0, sizeof(state_teris) / sizeof(state_teris[0]));
	int rd_next = RandomInt(0, sizeof(state_teris) / sizeof(state_teris[0]));
	//outPutBoxInt(rd_start);
	//outPutBoxInt(rd_next);
	//outPutBoxInt(rd_start);
	if (GAME_STATE == 0)
	{
		GAME_STATE = GAME_STATE | 0x0001;
		//outPutBoxInt(GAME_STATE);
		/*函数memcpy
		功能：从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
		函数原型：void *memcpy(void *dest, const void *src, size_t n);
		*/
		memcpy(bCurTeris, state_teris[rd_start], sizeof(state_teris[rd_start]));
		memcpy(bNextCurTeris, state_teris[rd_next], sizeof(state_teris[rd_next]));
	}
	else
	{
		memcpy(bCurTeris, bNextCurTeris, sizeof(bNextCurTeris));
		memcpy(bNextCurTeris, state_teris[rd_next], sizeof(state_teris[rd_next]));
	}

}
void init_game()//初始化
{
	GAME_SCORE = 0;
	setRandomT();//随机生成一个方块用作下一次掉落
	curPosX = (NUM_X - 4) >> 1;//设置初始横坐标x
	curPosY = 0;//设置初始纵坐标y

	/*函数memset
	功能：在一段内存块中填充某个给定的值，它是对较大的结构体或数组进行清零操作的一种最快方法
	函数原型：void *memset(void *s, int ch, size_t n);
	函数解释：将s中当前位置后面的n个字节 （typedef unsigned int size_t ）用 ch 替换并返回 s 
	*/
	memset(g_hasBlocked, 0, sizeof(g_hasBlocked));
	rc_left.left = 0;
	rc_left.right = SCREEN_LEFT_X;
	rc_left.top = 0;
	rc_left.bottom = SCREEN_Y;

	rc_right.left = rc_left.right + BORDER_X;
	rc_right.right = 180 + rc_right.left;
	rc_right.top = 0;
	rc_right.bottom = SCREEN_Y;

	rc_main.left = 0;
	rc_main.right = SCREEN_X;
	rc_main.top = 0;
	rc_main.bottom = SCREEN_Y;

	rc_right_top.left = rc_right.left;
	rc_right_top.top = rc_right.top;
	rc_right_top.right = rc_right.right;
	rc_right_top.bottom = (rc_right.bottom) / 2;

	rc_right_bottom.left = rc_right.left;
	rc_right_bottom.top = rc_right_top.bottom + BORDER_Y;
	rc_right_bottom.right = rc_right.right;
	rc_right_bottom.bottom = rc_right.bottom;

	g_speed = t_speed = 1000 - GAME_DIFF * 280;
}

void fillBlock()//到达底部后填充矩阵
{
	int i, j;
	for (i = 0; i<4; i++)
	{
		for (j = 0; j<4; j++)
		{
			if (bCurTeris[i][j])
			{
				g_hasBlocked[curPosY + i][curPosX + j] = 1;
			}
		}
	}
}

void RotateTeris(BOOL bTeris[4][4])//旋转矩阵
{
	BOOL bNewTeris[4][4];
	int x, y;
	for (x = 0; x<4; x++)
	{
		for (y = 0; y<4; y++)
		{
			bNewTeris[x][y] = bTeris[3 - y][x];
			//逆时针：
			//bNewTeris[x][y] = bTeris[y][3-x];
		}
	}
	if (CheckValide(curPosX, curPosY, bNewTeris) == 1)
	{
		memcpy(bTeris, bNewTeris, sizeof(bNewTeris));//成功则将变换后的形状保存
	}

}

int RandomInt(int _min, int _max)//获取一个随机int
{
	srand((rd_seed++) % 65532 + GetTickCount());
	return _min + rand() % (_max - _min);
}

VOID DrawTeris(HDC mdc)//绘制正在下落的方块
{

	int i, j;
	HPEN hPen = (HPEN)GetStockObject(BLACK_PEN);//设置画笔句柄
	HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);//设置画刷句柄
	SelectObject(mdc, hPen);//获取系统画笔句柄
	SelectObject(mdc, hBrush);//获取系统画刷句柄
	for (i = 0; i<4; i++)
	{
		for (j = 0; j<4; j++)
		{
			if (bCurTeris[i][j])
			{
				Rectangle(mdc, (j + curPosX)*BLOCK_SIZE + BORDER_X, (i + curPosY)*BLOCK_SIZE + BORDER_Y, (j + 1 + curPosX)*BLOCK_SIZE + BORDER_X, (i + 1 + curPosY)*BLOCK_SIZE + BORDER_Y);
			}
		}
	}
	drawBlocked(mdc);
	DeleteObject(hPen);
	DeleteObject(hBrush);
}

VOID DrawBackGround(HDC hdc)//绘制背景
{

	HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	HDC mdc = CreateCompatibleDC(hdc);//创建一个与指定设备兼容的内存设备上下文环境
	HBITMAP hBitmap = CreateCompatibleBitmap(hdc, SCREEN_X, SCREEN_Y);//创建与指定的设备环境相关的设备兼容的位图

	SelectObject(mdc, hBrush);
	SelectObject(mdc, hBitmap);

	HBRUSH hBrush2 = (HBRUSH)GetStockObject(WHITE_BRUSH);
	FillRect(mdc, &rc_main, hBrush2);//填充方块颜色
	Rectangle(mdc, rc_left.left + BORDER_X, rc_left.top + BORDER_Y, rc_left.right, rc_left.bottom);
	Rectangle(mdc, rc_right.left + BORDER_X, rc_right.top + BORDER_Y, rc_right.right, rc_right.bottom);
	DrawTeris(mdc);//绘制正在下落的方块
	drawNext(mdc);//绘制下一个将要掉落的方块
	drawScore(mdc);//绘制分数
	::BitBlt(hdc, 0, 0, SCREEN_X, SCREEN_Y, mdc, 0, 0, SRCCOPY);//SRCCOPY：将源矩形区域直接拷贝到目标矩形区域
	DeleteObject(hBrush);
	DeleteDC(mdc);
	DeleteObject(hBitmap);
	DeleteObject(hBrush2);


	//  int x,y;
	//  HPEN hPen = (HPEN)GetStockObject(NULL_PEN);
	//  HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	//  SelectObject(hdc,hPen);
	//  SelectObject(hdc,hBrush);
	//  for (x = 0;x<NUM_X;x++)
	//  {
	//      for(y=0;y<NUM_Y;y++)
	//      {
	//          Rectangle(hdc,BORDER_X+x*BLOCK_SIZE,BORDER_Y+y*BLOCK_SIZE,
	//              BORDER_X+(x+1)*BLOCK_SIZE,
	//              BORDER_Y+(y+1)*BLOCK_SIZE);
	//      }
	//  }
}

void drawNext(HDC hdc)//绘制下一个将要掉落的方块
{
	int i, j;
	HBRUSH hBrush = (HBRUSH)CreateSolidBrush(RGB(0, 188, 0));//取绿色画刷
	SelectObject(hdc, hBrush);
	for (i = 0; i<4; i++)
	{
		for (j = 0; j<4; j++)
		{
			if (bNextCurTeris[i][j])
			{
				Rectangle(hdc, rc_right_top.left + BLOCK_SIZE*(j + 1), rc_right_top.top + BLOCK_SIZE*(i + 1), rc_right_top.left + BLOCK_SIZE*(j + 2), rc_right_top.top + BLOCK_SIZE*(i + 2));
			}
		}
	}
	HFONT hFont = CreateFont(30, 0, 0, 0, FW_THIN, 0, 0, 0, UNICODE, 0, 0, 0, 0, L"微软雅黑");
	SelectObject(hdc, hFont);
	SetBkMode(hdc, TRANSPARENT);//设置指定DC的背景混合模式，背景混合模式用于与文本，填充画刷和当画笔不是实线时
	SetBkColor(hdc, RGB(255, 255, 0));//白色设置当前背景色
	RECT rect;
	rect.left = rc_right_top.left + 40;
	rect.top = rc_right_top.bottom - 150;
	rect.right = rc_right_top.right;
	rect.bottom = rc_right_top.bottom;
	DrawTextW(hdc, TEXT("下一个"), _tcslen(TEXT("下一个")), &rect, 0);
	DeleteObject(hFont);
	DeleteObject(hBrush);
}

void drawScore(HDC hdc)//绘制分数
{
	HFONT hFont = CreateFont(30, 0, 0, 0, FW_THIN, 0, 0, 0, UNICODE, 0, 0, 0, 0, L"微软雅黑");
	SelectObject(hdc, hFont);
	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 0));
	RECT rect;
	rect.left = rc_right_bottom.left;
	rect.top = rc_right_bottom.top;
	rect.right = rc_right_bottom.right;
	rect.bottom = rc_right_bottom.bottom;
	TCHAR szBuf[30];
	LPCTSTR cstr = TEXT("当前难度：%d");
	wsprintf(szBuf, cstr, GAME_DIFF);
	DrawTextW(hdc, szBuf, _tcslen(szBuf), &rect, DT_CENTER | DT_VCENTER);

	RECT rect2;
	rect2.left = rc_right_bottom.left;
	rect2.top = rc_right_bottom.bottom / 2 + 100;
	rect2.right = rc_right_bottom.right;
	rect2.bottom = rc_right_bottom.bottom;
	TCHAR szBuf2[30];
	LPCTSTR cstr2 = TEXT("得分：%d");
	wsprintf(szBuf2, cstr2, GAME_SCORE);
	//outPutBoxInt(sizeof(szBuf));
	DrawTextW(hdc, szBuf2, _tcslen(szBuf2), &rect2, DT_CENTER | DT_VCENTER);

	DeleteObject(hFont);
}

int selectDiffculty(HWND hWnd, int diff)//选择难度
{
	TCHAR szBuf2[30];
	LPCTSTR cstr2 = TEXT("确认选择难度 %d 吗？");
	wsprintf(szBuf2, cstr2, diff);
	if (MessageBox(hWnd, szBuf2, L"难度选择", MB_YESNO) == IDYES)
	{
		GAME_DIFF = diff;
		InvalidateRect(hWnd, &rc_right_bottom, false);
		GAME_STATE |= 2;
		init_game();
		return GAME_DIFF;
	}
	return -1;
}

int selectLayOut(HWND hWnd, int layout)//选择布局
{
	NUM_X = 10 * layout;
	NUM_Y = 20 * layout;
	BLOCK_SIZE = 30 / layout;
	GAME_STATE |= 2;
	InvalidateRect(hWnd, &rc_right_bottom, false);
	init_game();
	return layout;
}

void drawCompleteParticle(int line)
{
	HWND hWnd = GetActiveWindow();//获得与调用线程的消息队列相关的活动窗口的窗口
	HDC hdc = GetDC(hWnd);
	HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);//获取系统画刷
	HPEN hPen = (HPEN)CreatePen(PS_DOT, 1, RGB(255, 255, 0));//设置画笔句柄
	SelectObject(hdc, hBrush);
	SelectObject(hdc, hPen);
	Rectangle(hdc, BORDER_X,
		BORDER_Y + line*BLOCK_SIZE,
		BORDER_X + NUM_X*BLOCK_SIZE,
		BORDER_Y + BLOCK_SIZE*(1 + line));
	DeleteObject(hBrush);//删除画刷
	DeleteObject(hPen);//删除画笔
}
